package com.jt.aop;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.alibaba.druid.util.StringUtils;
import com.jt.anno.Cache_Find;
import com.jt.util.ObjectMapperUtil;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisCluster;
import redis.clients.jedis.ShardedJedis;

@Component//交给spring管理
@Aspect//标识切面类     切面=切入点+通知
public class CacheAspect {
	@Autowired(required = false)//表示调用时才注入，相当于延时加载@Lazy
	//private Jedis jedis;//redis单片
	//private ShardedJedis jedis;////redis分片
	//private Jedis jedis;//redis哨兵
	private JedisCluster jedis;//redis集群
	
	/**
	 * 环绕通知
	 * 1.返回值  必须为Object类型，表示执行完成业务之后返回给用户的数据
	 * 2.参数  a.必须位于第1位  b.参数类型必须为ProceedingJoinPoint（因为要用环绕通知控制目标方法执行，其他通知不能控制）
	 * 3.关于注解取值规则  springAOP中提供了可以直接获取注解的方法，但是要求参数的名称必须一致，否则映射错误
	 */
	//@Around("@annotation(com.jt.anno.Cache_Find)")//这样写下面就要通过反射获取注解的名称和参数key、seconds等
	@Around("@annotation(cacheFind)")
	public Object around(ProceedingJoinPoint joinPoint,Cache_Find cacheFind) {
		Object resultData=null;//结果数据，对象
		
		String key=getKey(joinPoint,cacheFind);
		String resultJSON=jedis.get(key);//从缓存中取数据，JSON串
		
		if (StringUtils.isEmpty(resultJSON)) {
			//表示缓存中没有数据则需要执行目标方法，从数据库查询数据
			try {
				resultData=joinPoint.proceed();//从数据库查询出来的数据，对象
				String value=ObjectMapperUtil.toJSON(resultData);//把数据库中查询出来的数据转换成JSON，要保存到缓存中
				//判断注解中是否要求数据永久保存，再保存到缓存中
				if (cacheFind.seconds()>0) {
					jedis.setex(key, cacheFind.seconds(), value);
				}else {
					jedis.set(key, value);
				}
				System.out.println("AOP查询数据库成功！");
			} catch (Throwable e) {
				e.printStackTrace();
				throw new RuntimeException(e);
			}
		}else {
			//获取目标方法的返回值类型
			Class returnType=getReturnType(joinPoint);
			//表示缓存中有数据，将resultJSON转换成对象并传给resultData
			resultData=ObjectMapperUtil.toObject(resultJSON, returnType);
			System.out.println("AOP查询缓存成功！");
		}
		return resultData;
	}
	
	/**
	 * 获取目标方法的返回值类型
	 * @param joinPoint
	 * @return
	 */
	private Class getReturnType(ProceedingJoinPoint joinPoint) {
		MethodSignature signature=(MethodSignature)joinPoint.getSignature();
		return signature.getReturnType();
	}

	/**
	 * key生成策略：
	 * 1.如果用户在注解中指定了key，则使用用户指定的
	 * 2.如果用户没指定，在使用自动生成的key  类名+方法名+目标方法第一个参数（parentId）
	 * @param joinPoint
	 * @param cacheFind
	 * @return
	 */
	private String getKey(ProceedingJoinPoint joinPoint, Cache_Find cacheFind) {
		String key=cacheFind.key();//默认值 ""
		if (StringUtils.isEmpty(key)) {
			//如果为空就自动生成
			String className=joinPoint.getSignature().getDeclaringTypeName();//类名
			String methodName=joinPoint.getSignature().getName();//方法名
			String arg1=String.valueOf(joinPoint.getArgs()[0]);//目标方法第一个参数
			//com.jt.controller.list::0 拼接格式
			return className+"."+methodName+"::"+arg1;
		} else {
			return key;
		}
	}
}
